#include "uthreads.hh"

#include "scheduler.hh"
#include <stdlib.h>
#include<iostream>
#include <sys/time.h>

static uScheduler* s=NULL;

void timer_handler(int signum)
{
	s->blockSignals();
	s->timer_handler(signum);
	s->UnBlockSignals();
}

/* Initialize thread library */
int thread_lib_init() {
	s->blockSignals();
	s= new uScheduler();
	s->initializeSignals(timer_handler);
	//signal(SIGVTALRM, timer_handler);
	s->UnBlockSignals();
	return 0;
}

/* Create a new thread whose entry point is f */
int thread_spawn(void (*thread_func)(void)) {
	s->blockSignals();
	try {
		int threadid = s->spawn_thread(thread_func);
		
		s->run_next_thread();
		s->UnBlockSignals();
		return threadid;
	} catch (const char *error ){
		std::cout << error << std::endl;
		s->UnBlockSignals();
		return -1;
	}
	s->UnBlockSignals();
}

/* Terminate a thread */
int thread_terminate(int tid) {
	s->blockSignals();
	try {
		if(tid==0) { //terminate the program!
			delete s;
			s->UnBlockSignals();
			exit(0);
		}
		s->UnBlockSignals();
		return s->terminate_thread(tid);
		
	} catch (const char *error ){
		std::cout << error << std::endl;
		s->UnBlockSignals();
		return -1;
	}
}

/* Synchronize threads */
int thread_sync(int sync_id, int num) {
	s->blockSignals();
	try {
		s->sync_thread(sync_id,num);
		s->UnBlockSignals();
		return 0;
	} catch (const char *error ){
		std::cout << error << std::endl;
		s->UnBlockSignals();
		return -1;
	}
}

/* Sleep */
int thread_sleep(int tid, int num_millisecs) {
	s->blockSignals();
	try{
		s->sleep_thread(tid,num_millisecs);
		s->UnBlockSignals();
		return 0;
	} catch (const char *error ){
		std::cout << error << std::endl;
		s->UnBlockSignals();
		return -1;
	}
}

/* Get thread id */
int thread_gettid() {
	try{
	s->blockSignals();
	int tid=s->get_current_thread();
	s->UnBlockSignals();
	return tid;
	} catch (const char *error ){
		std::cout << error << std::endl;
		s->UnBlockSignals();
		return -1;
	}
}

int div_ceil(int numerator,int denumerator) {
	if(numerator%denumerator==0)
		return numerator/denumerator;
	else
		return numerator/denumerator+1;
}

/* Get total quantums */
int thread_get_time_until_wakeup(int tid) {
	try {
		s->blockSignals();
		if(!s->contains_thread(tid)) throw "Scheduler: Thread doesn't exist!";
		if(s->get_sleeping_id()!=tid) return 0;
		itimerval it;
		if(getitimer(ITIMER_VIRTUAL,&it)==-1) throw "system error: Couldn't get timer value.\n";
		
		s->UnBlockSignals();
		return it.it_value.tv_sec*1000+ div_ceil(it.it_value.tv_usec,1000);
	} catch (const char *error ){
		std::cout << error << std::endl;
		s->UnBlockSignals();
		return -1;
	}
	
}



/* Get current thread's quantums */
int thread_get_num_waiting_for_sync(int sync_id) {
	return s->getThreadsWaiting(sync_id);
}
